﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Threading;
using e77.Ud2.HeadEnums;
using e77.Ud2;

namespace Szakdolgozat
{
    public class Command : IHead
    {

        string[] availableports;
        byte[] head = new byte[] { 0xAA, 0x55 };
        public static SerialPort usedserialport = new SerialPort();
        public static string usedPort = "";


        // ellenőrzési célból a Send is visszatér egy byte[]-el, a kiküldött adatokkal
        // params kulcsszó és object array paraméterrel lehet azt megcsinálni, hogy ne legyen második paramétere
        // feltétlen a metódusnak (mint pl verziólekérés)
        public byte[] Send(ECommand command, params object[] parameters)
        {
            // így megvan a parancs byte-á alakított változata
            byte[] bytecommand = { (byte)command };
            // az adatmező hossza byte-okban ( parameters-től függ)
            byte[] paramslength;
            byte[] paramsarray = null;
            List<byte[]> paramslist = new List<byte[]>();
            byte[] data;
            int i = 0;

            if (command == ECommand.FastMeasure || command == ECommand.LedOn || command == ECommand.Measure)
            {
                foreach (var param in parameters)
                {
                    byte[] temp = { Convert.ToByte(param) };
                    paramslist.Add(temp);
                }
            }
            else
            {
                foreach (var param in parameters)
                {
                    paramslist.Add(BitConverter.GetBytes(Convert.ToInt16(param)));
                }

            }
            foreach (var param in paramslist)
            {
                paramsarray = new byte[i + 2];
                param.CopyTo(paramsarray, i);
                i++;
                i++;
            }

            // a paramslist.count értéket szorozn kell 2-vel mivel 2 byte-n ábrázolt számokról van szó
            // kivéve, ha pl. LED maszk érkezik, az csak egy byte-n ábrázolt, tehát a paramsarray-ben tárolt
            // értékek mennyisége pont jó lesz


            //összefűzi a parancskódot, a hosszt és az (esetleges) paramétereket
            if (paramsarray == null)        // ebben az esetben nem jött paraméter, pl. SWverziónál, az adat hossza nulla 
            {
                paramslength = new byte[] { (byte)0 };
                data = head.Concat(bytecommand).Concat(paramslength).ToArray();
            }
            else
            {
                paramslength = new byte[] { (byte)(paramsarray.Count()) };
                data = head.Concat(bytecommand).Concat(paramslength).Concat(paramsarray).ToArray();
            }

            byte crc = 0;

            foreach (var item in data)
            {
                if (item == (byte)0xAA || item == (byte)0x55)
                    Console.WriteLine("head: {0}", item);
                else
                    crc ^= item;
            }


            byte[] outputBuffer = data.Concat(new byte[] { crc }).ToArray();

            // paraméterként a nulla az ofset, kiküldi soros portra az adatot
            int a = outputBuffer.Length;
            usedserialport.Open();
            usedserialport.Write(outputBuffer, 0, outputBuffer.Length);
            Thread.Sleep(250);
            return outputBuffer;

        }

        public byte[] Receive()
        {
            byte[] b = new byte[usedserialport.BytesToRead];

            usedserialport.Read(b, 0, b.Length);
            usedserialport.Close();
            Thread.Sleep(500);

            return b;
        }


        // kiküldi az elérhető soros portokra a HEAD_VERSION-t, a megfelelő válaszú lesz a fej, de pontosan egy lehet csak belőle
        public string Portselect()
        {
            byte[] sent;
            byte[] received;
            List<string> ports = new List<string>();

            // visszaadja a géphez csatlakoztatott elérhető soros portok listáját, string[] a visszatérési értéke
            availableports = SerialPort.GetPortNames();

            // a usedserialport változót egyszer kell inicalizálni, hiszen minden portra ugyanazt kell kiküldeni
            this.SerialPortSet(usedserialport);

            // egy port változót használ a tesztadatok kiküldésére
            foreach (var item in availableports)
            {
                // itt a folyamatos névállítgatás a Send() metódus miatt szükséges
                usedserialport.PortName = item;
                //  usedserialport.Open();
                sent = Send(ECommand.Version);
                received = Receive();
                usedserialport.Close();
                // ha egyezik az első három byte, akkor jó fejnek küldi az adatot, számlálás ahhoz kell, hogy egy fej legyen csak
                // a jó COM portok nevét menti
                if (QAequals(sent, received))
                {
                    ports.Add(usedserialport.PortName);
                }
            }

            // ebben az esetben csak egy mérőfej van csatlakoztatva, itt inicalizálja a portot is
            if (ports.Count == 1)
            {
                usedPort = ports.ElementAt(0);
                usedserialport.PortName = usedPort;
                return usedPort;
            }
            else if (ports.Count == 0)
            {

                Console.WriteLine("Please insert a measure head!");
                return "none";
            }
            else
            {
                Console.WriteLine("Please insert only one measure head!");
                return "onlyone";
            }

        }

        public bool QAequals(byte[] a, byte[] b)
        {

            // így már végig lehet lépkedni egyesével a byte-okon
            a.ToArray();
            b.ToArray();

            // először megvizsgálom, hogy a és b nem nulla elemet tartalmaz-e 
            // nyilván az tartalmaz nulla elemet, amelyiket az a port adta vissza, ahol nem a mérőfej van
            if (a.Length != 0 && b.Length != 0)
            {

                for (int i = 0; i < 2; i++)
                {
                    if (a[i] != b[i])
                        return false;
                }
                return true;
            }
            else
                return false;
        }

        // a megadott paraméterek beállításra kerülnek, csak így lehet adatot küldeni, portnyitás csak adatküldéskor
        public void SerialPortSet(SerialPort serialport)
        {
            serialport.BaudRate = 19200;
            serialport.DataBits = 8;
            serialport.Parity = Parity.None;
            serialport.StopBits = StopBits.One;
            serialport.ReadTimeout = 250;
            serialport.WriteTimeout = 250;
        }

        // a következő metódushoz: a mérés során a LED maszkot paraméteresen kapja a fej: 
        // a HeadEnums-ban definiálva vannak az egyes anyagokhoz a maszkok, és ez fog hozzárendelődni a pad-hez
        // a HeadEnums-ból csak Array-t lehetett csinálni, de külön Array-ben van a név és az érték
        // így a pad név alapján meg kell keresni az egyik 

        //public void MeasureCommand(List<byte[]> results)
        //{

        //    LabStripU11 strip = new LabStripU11();
        //    this.StepMeasure(strip, results);

        //}

        // Init pozícióba lépteti a fejet
        public byte[] StepInit()
        {
            this.Send(ECommand.StripMoveAbs, e77.Ud2.Strip.Instance.Start_Position);
            return this.Receive();
        }

        // a paraméterként megadott pozícióba lépteti a fejet
        public byte[] PosStep(int pos)
        {
            this.Send(ECommand.StripMoveAbs, pos);
            return this.Receive();
        }

        // adott pozícióban levő pad lemérése
        public byte[] PosMeasure(ELedMask mask)
        {
            this.Send(ECommand.Measure, mask);
            return this.Receive();
        }


    }
}







